This page last changed on Jun 23, 2008 by stepheneb.

OTrunk configurations are created by making OTML files that specify the existance and behaviour of each of the OTrunk components you want to include. OTML is an extensible declarative xml-based language for both layout and composition of content as well as for object state persistence (learner data).

The simplest OTML file must specify an OTrunk, declare a number of imports (the OTrunk components being used), and specify the location and behaviour of each of the components.

Hello World example

In this example we will create a OTrunk Viewer containing a simple text object.

You can run it here: text-label.otml

text-label.otml
<?xml version="1.0" encoding="UTF-8"?>
<otrunk id="33754150-b594-11d9-9669-0800200c9a66">

  <imports>
    <import class="org.concord.otrunk.OTSystem"/>
    <import class="org.concord.otrunk.view.OTViewEntry"/>
    <import class="org.concord.otrunk.view.OTViewBundle"/>
    <import class="org.concord.otrunk.ui.OTText"/>
  </imports>

  <objects>
    <OTSystem>

      <bundles>
        <OTViewBundle>
          <viewEntries>
            <OTViewEntry
              objectClass="org.concord.otrunk.ui.OTText"
              viewClass="org.concord.otrunk.ui.swing.OTTextView"/>
          </viewEntries>
        </OTViewBundle>
      </bundles>

      <root>
        <OTText name="Text Object">
          <text>
            Hello World!
          </text>
        </OTText>
      </root>

    </OTSystem>
  </objects>
</otrunk>

You can assume as a convention that a name beginning with an uppercase letter represents an object with a subtype of OTObject, and one in camel case that begins with a lowercase is a property of the enclosing object. For example, in the otml above, we can tell that "text" is a property of OTText that can be retrieved by calling t.getText() for some object t of type OTText.

Let's begin from the end product: we want to show "Hello World!" in the application window.

Let's regard "Hello World!" as data for the model (as in the Model/View/Controller architecture). We need to define both the model and the view for it. When otrunk process an ot object, it finds the corresponding view from the <viewEntries> section of the otml. (For more see OTrunk View System.)

The "root object", usually put under /otrunk/objects/OTSystem/root/, and its view, encompasses everything displayed in the client area. (Be careful to distinguish it from "root element of the xml", which is <otrunk> here).

Here we used OTText as our root object. When otrunk reads in the otml and runs across the <OTText> tag, it knows how to construct an object that implements the OTText interface so that the "view" code can call getText() on it to retrieve the text which is "Hello World!".

In order to tell otrunk what we exactly mean by OTText though, we need the <import> element in the <imports> section that shows the fully qualified name (org.concord.otrunk.ui.OTText). (Same for all the other ot classes that appear in the otml.)

Note that OTText is mapped to OTTextView in an OTViewEntry. If you look into the source code of OTTextView, you'll find the getComponent() method that returns a swing label that contains the text obtained from the OTText object.

Slider Data Graph Example:

Here's a slightly more advanced example where the property data producer for a OTDataCollector graph comes not from a sensor but instead from the movement of a slider component.

You can run it here: slider_data_graph.otml.

Inside the bodyText (lines 31..40) is a simple html table with object references in two cells. The embedded object in line 34 is a OTDataCollector (line 44). This type of object is most often used to collect data from probes or to allow a student to sketch a dataset however in this case the OTPropertyDataProducer is setup to read inputs from the slider.

<dataProducer>
  <OTPropertyDataProducer timeScale="0.1" property="value" target="${slider}"/>
</dataProducer>

The slider (line 61) has a very simple set of attributes:

  • local_id: "slider"
  • name: "Slider"
  • minimum: -10
  • maximum: -10
  • value
    The only one a learner can change is the value which is sent as a data stream to the OTDataCollector. Any changes to the actual value of the slider will get persisted at the end of the session as leaner data.

Here's what a running instance of the activity looks like:

Here's what the otml (xml) looks like:

<?xml version="1.0" encoding="UTF-8"?>
<otrunk id="33754150-b594-11d9-9669-0800200c9a66">
  <imports>
    <import class="org.concord.otrunk.OTSystem"/>
    <import class="org.concord.otrunk.view.OTViewBundle"/>
    <import class="org.concord.otrunk.view.OTViewEntry"/>
    <import class="org.concord.otrunk.view.document.OTCompoundDoc"/>
    <import class="org.concord.data.state.OTPropertyDataProducer"/>
    <import class="org.concord.datagraph.state.OTDataAxis"/>
    <import class="org.concord.datagraph.state.OTDataGraphable"/>
    <import class="org.concord.datagraph.state.OTDataCollector"/>
    <import class="org.concord.otrunk.ui.OTSlider"/>
  </imports>
  <objects>
    <OTSystem>
      <bundles>
        <OTViewBundle>
          <views>
            <OTViewEntry objectClass="org.concord.otrunk.view.document.OTCompoundDoc"
               viewClass="org.concord.otrunk.view.document.OTDocumentView"/>
            <OTViewEntry objectClass="org.concord.datagraph.state.OTDataCollector" 
              viewClass="org.concord.datagraph.state.OTDataCollectorView"/>
            <OTViewEntry objectClass="org.concord.otrunk.ui.OTSlider" 
              viewClass="org.concord.otrunk.ui.swing.OTSliderView"/>
          </views>
        </OTViewBundle>
      </bundles>
      <root>
        <OTCompoundDoc name="Compound Document">
          <bodyText>
            <table border="1">
              <tr>
                <td>
                  <object refid="${graph}"/>
                </td>
                <td>
                  <object refid="${slider}"/>
                </td>
              </tr>
            </table>
          </bodyText>
          <children>
            <!--  the graph which displays the value of the slider -->
            <OTDataCollector local_id="graph" name="Graph">
              <source>
                <OTDataGraphable connectPoints="true" color="0x0000ff" drawMarks="false" 
                  name="Slider Value" xColumn="0" yColumn="1">
                  <dataProducer>
                    <OTPropertyDataProducer timeScale="0.1" property="value" target="${slider}"/>
                  </dataProducer>
                </OTDataGraphable>
              </source>
              <xDataAxis>
                <OTDataAxis min="0" max="100" label="Time" units="min"/>
              </xDataAxis>
              <yDataAxis>
                <OTDataAxis min="-10" max="10" label="sin(x)"/>
              </yDataAxis>
            </OTDataCollector>
            <!--  the slider itself -->
            <OTSlider local_id="slider" name="Slider" minimum="-10" maximum="10" value="0"/>
          </children>
        </OTCompoundDoc>
      </root>
    </OTSystem>
  </objects>
</otrunk>

See OTrunk Introduction for an introduction to otrunk.

See OTrunk for advanced topics for otrunk.

Link to source code:


slider-datagraph.png (image/png)
Document generated by Confluence on Jan 27, 2014 16:52